package com.morty.jwt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.morty.pojo.Result;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

/**
 * @Author:
 * @Date:
 * @Description: jwt过滤器
 */
@Setter
@Getter
@Slf4j
public class JwtTokenFilter implements GlobalFilter, Ordered {

	@Value("${jwt.skip-auth-urls}")
	private String[] skipAuthUrls;

	private final String API_DOC_SUBFFIX= "api-docs";
	private final String API_DOC_DEFAULT_TOKEN= "pms-api-token";

	private ObjectMapper objectMapper;
	private RedisTemplate redisTemplate;

	public JwtTokenFilter(ObjectMapper objectMapper, RedisTemplate redisTemplate) {
		this.objectMapper = objectMapper;
		this.redisTemplate = redisTemplate;
	}

	/**
	 * 过滤器
	 * @param exchange
	 * @param chain
	 * @return
	 */
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		String url = exchange.getRequest().getURI().getPath();
		//跳过不需要验证的路径
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        if (Stream.of(skipAuthUrls).anyMatch(s -> antPathMatcher.match(s, url))) {
            return chain.filter(exchange);
        }
		//跳过swaggerUI接口访问拦截
		if (url.contains(API_DOC_SUBFFIX)) {
			return chain.filter(exchange);
		}
		//获取token
		String token = exchange.getRequest().getHeaders().getFirst("Authorization");
		ServerHttpResponse resp = exchange.getResponse();
		if(StringUtils.isBlank(token)){
			//没有token
			return authError(resp,"非法访问");
		}else{
			//有token
			try {
				//解析token
				boolean checkRes = JwtUtil.checkJWT(token);
				if (!checkRes) {
					return authError(resp,"认证过期");
				}
				//刷新缓存过期时间
				// TODO 临时将登录token续签时间调整为12H
				redisTemplate.expire(token, 12, TimeUnit.HOURS);
				return chain.filter(exchange);
			}catch (ExpiredJwtException e){
				log.error(e.getMessage(),e);
				if(e.getMessage().contains("Allowed clock skew")){
					return authError(resp,"认证过期");
				}else{
					return authError(resp,"认证失败");
				}
			}catch (Exception e) {
				log.error(e.getMessage(),e);
				return authError(resp,"认证失败");
			}
		}
	}

	/**
	 * 认证错误输出
	 * @param resp 响应对象
	 * @param mess 错误信息
	 * @return
	 */
	private Mono<Void> authError(ServerHttpResponse resp, String mess) {
		resp.setStatusCode(HttpStatus.UNAUTHORIZED);
		resp.getHeaders().add("Content-Type","application/json;charset=UTF-8");
		Result<String> returnData = new Result<>(org.apache.http.HttpStatus.SC_UNAUTHORIZED, mess, mess);
		String returnStr = "";
		try {
			returnStr = objectMapper.writeValueAsString(returnData);
		} catch (JsonProcessingException e) {
			log.error(e.getMessage(),e);
		}
		DataBuffer buffer = resp.bufferFactory().wrap(returnStr.getBytes(StandardCharsets.UTF_8));
		return resp.writeWith(Flux.just(buffer));
	}

	@Override
	public int getOrder() {
		return -100;
	}
}
